import 'package:flutter/material.dart';

class ModalBottomSheet<T> extends StatefulWidget {
  const ModalBottomSheet({Key key, this.route}) : super(key: key);

  final ModalBottomSheetRoute<T> route;

  @override
  ModalBottomSheetState<T> createState() => ModalBottomSheetState<T>();
}

class ModalBottomSheetState<T> extends State<ModalBottomSheet<T>> {
  @override
  Widget build(BuildContext context) {
    final MediaQueryData mediaQuery = MediaQuery.of(context);
    return GestureDetector(
        excludeFromSemantics: true,
        onTap: (){
         // Navigator.pop(context);
        },
        child: AnimatedBuilder(
            animation: widget.route.animation,
            builder: (BuildContext context, Widget child) {
              // Disable the initial animation when accessible navigation is on so
              // that the semantics are added to the tree at the correct time.
              final double animationValue = mediaQuery.accessibleNavigation
                  ? 1.0
                  : widget.route.animation.value;
              return Semantics(
                scopesRoute: true,
                namesRoute: true,
                explicitChildNodes: true,
                child: ClipRect(
                  child: CustomSingleChildLayout(
                    delegate: ModalBottomSheetLayout(animationValue),
                    child: BottomSheet(
                      animationController: widget.route._animationController,
                      onClosing: () => Navigator.pop(context),
                      builder: widget.route.builder,
                    ),
                  ),
                ),
              );
            }));
  }
}

class ModalBottomSheetLayout extends SingleChildLayoutDelegate {
  ModalBottomSheetLayout(this.progress);

  final double progress;

  @override
  BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
    return BoxConstraints(
        minWidth: constraints.maxWidth,
        maxWidth: constraints.maxWidth,
        minHeight: 0.0,
        maxHeight: constraints.maxHeight * 9.0 / 16.0);
  }

  @override
  Offset getPositionForChild(Size size, Size childSize) {
    return Offset(0.0, size.height - childSize.height * progress);
  }

  @override
  bool shouldRelayout(ModalBottomSheetLayout oldDelegate) {
    return progress != oldDelegate.progress;
  }
}

class ModalBottomSheetRoute<T> extends PopupRoute<T> {
  ModalBottomSheetRoute({
    this.builder,
    this.theme,
    bool barrierDismissible,
    this.barrierLabel,
    RouteSettings settings,
  }) : super(settings: settings) {
    barrierDismissibleCore = barrierDismissible;
  }

  bool barrierDismissibleCore = false;
  final WidgetBuilder builder;
  final ThemeData theme;

  @override
  Duration get transitionDuration => Duration(milliseconds: 200);

  @override
  bool get barrierDismissible => barrierDismissibleCore;

  @override
  final String barrierLabel;

  @override
  Color get barrierColor => Colors.black54;

  AnimationController _animationController;

  @override
  AnimationController createAnimationController() {
    assert(_animationController == null);
    _animationController =
        BottomSheet.createAnimationController(navigator.overlay);
    return _animationController;
  }

  @override
  Widget buildPage(BuildContext context, Animation<double> animation,
      Animation<double> secondaryAnimation) {
    // By definition, the bottom sheet is aligned to the bottom of the page
    // and isn't exposed to the top padding of the MediaQuery.
    Widget bottomSheet = MediaQuery.removePadding(
      context: context,
      removeTop: true,
      child: ModalBottomSheet<T>(route: this),
    );
    if (theme != null) bottomSheet = Theme(data: theme, child: bottomSheet);
    return bottomSheet;
  }
}

Future<T> showModalBottom<T>(
    {@required BuildContext context,
    @required WidgetBuilder builder,
    barrierDismissible: false}) {
  return Navigator.push(
      context,
      ModalBottomSheetRoute<T>(
          builder: builder,
          theme: Theme.of(context, shadowThemeOnly: true),
          barrierLabel:
              MaterialLocalizations.of(context).modalBarrierDismissLabel,
          barrierDismissible: barrierDismissible));
}
